/*
 * Copyright 2002-2014, Haiku, Inc.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Ingo Weinhold, ingo_weinhold@gmx.de
 */


#include <new>
#include <set>
#include <stdlib.h>
#include <string>

#include <AppFileInfo.h>
#include <Bitmap.h>
#include <File.h>
#include <fs_attr.h>
#include <IconUtils.h>
#include <MimeType.h>
#include <RegistrarDefs.h>
#include <Resources.h>
#include <Roster.h>
#include <String.h>


// debugging
//#define DBG(x) x
#define DBG(x)
#define OUT	printf


// type codes
enum {
	B_APP_FLAGS_TYPE	= 'APPF',
	B_VERSION_INFO_TYPE	= 'APPV',
};


// attributes
static const char* kTypeAttribute				= "BEOS:TYPE";
static const char* kSignatureAttribute			= "BEOS:APP_SIG";
static const char* kAppFlagsAttribute			= "BEOS:APP_FLAGS";
static const char* kSupportedTypesAttribute		= "BEOS:FILE_TYPES";
static const char* kVersionInfoAttribute		= "BEOS:APP_VERSION";
static const char* kMiniIconAttribute			= "BEOS:M:";
static const char* kLargeIconAttribute			= "BEOS:L:";
static const char* kIconAttribute				= "BEOS:";
static const char* kStandardIconType			= "STD_ICON";
static const char* kIconType					= "ICON";
static const char* kCatalogEntryAttribute		= "SYS:NAME";

// resource IDs
static const int32 kTypeResourceID				= 2;
static const int32 kSignatureResourceID			= 1;
static const int32 kAppFlagsResourceID			= 1;
static const int32 kSupportedTypesResourceID	= 1;
static const int32 kMiniIconResourceID			= 101;
static const int32 kLargeIconResourceID			= 101;
static const int32 kIconResourceID				= 101;
static const int32 kVersionInfoResourceID		= 1;
static const int32 kMiniIconForTypeResourceID	= 0;
static const int32 kLargeIconForTypeResourceID	= 0;
static const int32 kIconForTypeResourceID		= 0;
static const int32 kCatalogEntryResourceID		= 1;

// R5 also exports these (Tracker is using them):
// (maybe we better want to drop them silently and declare
// the above in a public Haiku header - and use that one in
// Tracker when compiled for Haiku)
extern const uint32 MINI_ICON_TYPE, LARGE_ICON_TYPE;
const uint32 MINI_ICON_TYPE = 'MICN';
const uint32 LARGE_ICON_TYPE = 'ICON';


BAppFileInfo::BAppFileInfo()
	:
	fResources(NULL),
	fWhere(B_USE_BOTH_LOCATIONS)
{
}


BAppFileInfo::BAppFileInfo(BFile* file)
	:
	fResources(NULL),
	fWhere(B_USE_BOTH_LOCATIONS)
{
	SetTo(file);
}


BAppFileInfo::~BAppFileInfo()
{
	delete fResources;
}


status_t
BAppFileInfo::SetTo(BFile* file)
{
	// unset the old file
	BNodeInfo::SetTo(NULL);
	if (fResources) {
		delete fResources;
		fResources = NULL;
	}

	// check param
	status_t error
		= file != NULL && file->InitCheck() == B_OK ? B_OK : B_BAD_VALUE;

	info_location where = B_USE_BOTH_LOCATIONS;

	// create resources
	if (error == B_OK) {
		fResources = new(std::nothrow) BResources();
		if (fResources) {
			error = fResources->SetTo(file);
			if (error != B_OK) {
				// no resources - this is no critical error, we'll just use
				// attributes only, then
				where = B_USE_ATTRIBUTES;
				error = B_OK;
			}
		} else
			error = B_NO_MEMORY;
	}

	// set node info
	if (error == B_OK)
		error = BNodeInfo::SetTo(file);

	if (error != B_OK || (where & B_USE_RESOURCES) == 0) {
		delete fResources;
		fResources = NULL;
	}

	// clean up on error
	if (error != B_OK) {
		if (InitCheck() == B_OK)
			BNodeInfo::SetTo(NULL);
	}

	// set data location
	if (error == B_OK)
		SetInfoLocation(where);

	// set error
	fCStatus = error;
	return error;
}


status_t
BAppFileInfo::GetType(char* type) const
{
	// check param and initialization
	status_t error = type != NULL ? B_OK : B_BAD_VALUE;
	if (error == B_OK && InitCheck() != B_OK)
		error = B_NO_INIT;
	// read the data
	size_t read = 0;
	if (error == B_OK) {
		error = _ReadData(kTypeAttribute, kTypeResourceID, B_MIME_STRING_TYPE,
			type, B_MIME_TYPE_LENGTH, read);
	}
	// check the read data -- null terminate the string
	if (error == B_OK && type[read - 1] != '\0') {
		if (read == B_MIME_TYPE_LENGTH)
			error = B_ERROR;
		else
			type[read] = '\0';
	}
	return error;
}


status_t
BAppFileInfo::SetType(const char* type)
{
	// check initialization
	status_t error = B_OK;
	if (InitCheck() != B_OK)
		error = B_NO_INIT;
	if (error == B_OK) {
		if (type != NULL) {
			// check param
			size_t typeLen = strlen(type);
			if (typeLen >= B_MIME_TYPE_LENGTH)
				error = B_BAD_VALUE;
			// write the data
			if (error == B_OK) {
				error = _WriteData(kTypeAttribute, kTypeResourceID,
					B_MIME_STRING_TYPE, type, typeLen + 1);
			}
		} else
			error = _RemoveData(kTypeAttribute, B_MIME_STRING_TYPE);
	}
	return error;
}


status_t
BAppFileInfo::GetSignature(char* signature) const
{
	// check param and initialization
	status_t error = (signature ? B_OK : B_BAD_VALUE);
	if (error == B_OK && InitCheck() != B_OK)
		error = B_NO_INIT;
	// read the data
	size_t read = 0;
	if (error == B_OK) {
		error = _ReadData(kSignatureAttribute, kSignatureResourceID,
			B_MIME_STRING_TYPE, signature, B_MIME_TYPE_LENGTH, read);
	}
	// check the read data -- null terminate the string
	if (error == B_OK && signature[read - 1] != '\0') {
		if (read == B_MIME_TYPE_LENGTH)
			error = B_ERROR;
		else
			signature[read] = '\0';
	}
	return error;
}


status_t
BAppFileInfo::SetSignature(const char* signature)
{
	// check initialization
	status_t error = B_OK;
	if (InitCheck() != B_OK)
		error = B_NO_INIT;
	if (error == B_OK) {
		if (signature) {
			// check param
			size_t signatureLen = strlen(signature);
			if (signatureLen >= B_MIME_TYPE_LENGTH)
				error = B_BAD_VALUE;
			// write the data
			if (error == B_OK) {
				error = _WriteData(kSignatureAttribute, kSignatureResourceID,
					B_MIME_STRING_TYPE, signature, signatureLen + 1);
			}
		} else
			error = _RemoveData(kSignatureAttribute, B_MIME_STRING_TYPE);
	}
	return error;
}


status_t
BAppFileInfo::GetCatalogEntry(char* catalogEntry) const
{
	if (catalogEntry == NULL)
		return B_BAD_VALUE;

	if (InitCheck() != B_OK)
		return B_NO_INIT;

	size_t read = 0;
	status_t error = _ReadData(kCatalogEntryAttribute, kCatalogEntryResourceID,
		B_STRING_TYPE, catalogEntry, B_MIME_TYPE_LENGTH * 3, read);

	if (error != B_OK)
		return error;

	if (read >= B_MIME_TYPE_LENGTH * 3)
		return B_ERROR;

	catalogEntry[read] = '\0';

	return B_OK;
}


status_t
BAppFileInfo::SetCatalogEntry(const char* catalogEntry)
{
	if (InitCheck() != B_OK)
		return B_NO_INIT;

	if (catalogEntry == NULL)
		return _RemoveData(kCatalogEntryAttribute, B_STRING_TYPE);

	size_t nameLength = strlen(catalogEntry);
	if (nameLength > B_MIME_TYPE_LENGTH * 3)
		return B_BAD_VALUE;

	return _WriteData(kCatalogEntryAttribute, kCatalogEntryResourceID,
		B_STRING_TYPE, catalogEntry, nameLength + 1);
}


status_t
BAppFileInfo::GetAppFlags(uint32* flags) const
{
	// check param and initialization
	status_t error = flags != NULL ? B_OK : B_BAD_VALUE;
	if (error == B_OK && InitCheck() != B_OK)
		error = B_NO_INIT;
	// read the data
	size_t read = 0;
	if (error == B_OK) {
		error = _ReadData(kAppFlagsAttribute, kAppFlagsResourceID,
			B_APP_FLAGS_TYPE, flags, sizeof(uint32), read);
	}
	// check the read data
	if (error == B_OK && read != sizeof(uint32))
		error = B_ERROR;
	return error;
}


status_t
BAppFileInfo::SetAppFlags(uint32 flags)
{
	// check initialization
	status_t error = B_OK;
	if (InitCheck() != B_OK)
		error = B_NO_INIT;
	if (error == B_OK) {
		// write the data
		error = _WriteData(kAppFlagsAttribute, kAppFlagsResourceID,
			B_APP_FLAGS_TYPE, &flags, sizeof(uint32));
	}
	return error;
}


status_t
BAppFileInfo::RemoveAppFlags()
{
	// check initialization
	status_t error = B_OK;
	if (InitCheck() != B_OK)
		error = B_NO_INIT;
	if (error == B_OK) {
		// remove the data
		error = _RemoveData(kAppFlagsAttribute, B_APP_FLAGS_TYPE);
	}
	return error;
}


status_t
BAppFileInfo::GetSupportedTypes(BMessage* types) const
{
	// check param and initialization
	status_t error = types != NULL ? B_OK : B_BAD_VALUE;
	if (error == B_OK && InitCheck() != B_OK)
		error = B_NO_INIT;
	// read the data
	size_t read = 0;
	void* buffer = NULL;
	if (error == B_OK) {
		error = _ReadData(kSupportedTypesAttribute, kSupportedTypesResourceID,
			B_MESSAGE_TYPE, NULL, 0, read, &buffer);
	}
	// unflatten the buffer
	if (error == B_OK)
		error = types->Unflatten((const char*)buffer);
	// clean up
	free(buffer);
	return error;
}


status_t
BAppFileInfo::SetSupportedTypes(const BMessage* types, bool updateMimeDB,
	bool syncAll)
{
	// check initialization
	status_t error = B_OK;
	if (InitCheck() != B_OK)
		error = B_NO_INIT;

	BMimeType mimeType;
	if (error == B_OK)
		error = GetMetaMime(&mimeType);

	if (error == B_OK || error == B_ENTRY_NOT_FOUND) {
		error = B_OK;
		if (types) {
			// check param -- supported types must be valid
			const char* type;
			for (int32 i = 0;
				 error == B_OK && types->FindString("types", i, &type) == B_OK;
				 i++) {
				if (!BMimeType::IsValid(type))
					error = B_BAD_VALUE;
			}

			// get flattened size
			ssize_t size = 0;
			if (error == B_OK) {
				size = types->FlattenedSize();
				if (size < 0)
					error = size;
			}

			// allocate a buffer for the flattened data
			char* buffer = NULL;
			if (error == B_OK) {
				buffer = new(std::nothrow) char[size];
				if (!buffer)
					error = B_NO_MEMORY;
			}

			// flatten the message
			if (error == B_OK)
				error = types->Flatten(buffer, size);

			// write the data
			if (error == B_OK) {
				error = _WriteData(kSupportedTypesAttribute,
					kSupportedTypesResourceID, B_MESSAGE_TYPE, buffer, size);
			}

			delete[] buffer;
		} else
			error = _RemoveData(kSupportedTypesAttribute, B_MESSAGE_TYPE);

		// update the MIME database, if the app signature is installed
		if (updateMimeDB && error == B_OK && mimeType.IsInstalled())
			error = mimeType.SetSupportedTypes(types, syncAll);
	}
	return error;
}


status_t
BAppFileInfo::SetSupportedTypes(const BMessage* types, bool syncAll)
{
	return SetSupportedTypes(types, true, syncAll);
}


status_t
BAppFileInfo::SetSupportedTypes(const BMessage* types)
{
	return SetSupportedTypes(types, true, false);
}


bool
BAppFileInfo::IsSupportedType(const char* type) const
{
	status_t error = type != NULL ? B_OK : B_BAD_VALUE;
	// get the supported types
	BMessage types;
	if (error == B_OK)
		error = GetSupportedTypes(&types);
	// turn type into a BMimeType
	BMimeType mimeType;
	if (error == B_OK)
		error = mimeType.SetTo(type);
	// iterate through the supported types
	bool found = false;
	if (error == B_OK) {
		const char* supportedType;
		for (int32 i = 0;
			 !found && types.FindString("types", i, &supportedType) == B_OK;
			 i++) {
			found = strcmp(supportedType, "application/octet-stream") == 0
				|| BMimeType(supportedType).Contains(&mimeType);
		}
	}
	return found;
}


bool
BAppFileInfo::Supports(BMimeType* type) const
{
	status_t error
		= type != NULL && type->InitCheck() == B_OK ? B_OK : B_BAD_VALUE;
	// get the supported types
	BMessage types;
	if (error == B_OK)
		error = GetSupportedTypes(&types);
	// iterate through the supported types
	bool found = false;
	if (error == B_OK) {
		const char* supportedType;
		for (int32 i = 0;
			 !found && types.FindString("types", i, &supportedType) == B_OK;
			 i++) {
			found = BMimeType(supportedType).Contains(type);
		}
	}
	return found;
}


status_t
BAppFileInfo::GetIcon(BBitmap* icon, icon_size which) const
{
	return GetIconForType(NULL, icon, which);
}


status_t
BAppFileInfo::GetIcon(uint8** data, size_t* size) const
{
	return GetIconForType(NULL, data, size);
}


status_t
BAppFileInfo::SetIcon(const BBitmap* icon, icon_size which, bool updateMimeDB)
{
	return SetIconForType(NULL, icon, which, updateMimeDB);
}


status_t
BAppFileInfo::SetIcon(const BBitmap* icon, icon_size which)
{
	return SetIconForType(NULL, icon, which, true);
}


status_t
BAppFileInfo::SetIcon(const uint8* data, size_t size, bool updateMimeDB)
{
	return SetIconForType(NULL, data, size, updateMimeDB);
}


status_t
BAppFileInfo::SetIcon(const uint8* data, size_t size)
{
	return SetIconForType(NULL, data, size, true);
}


status_t
BAppFileInfo::GetVersionInfo(version_info* info, version_kind kind) const
{
	// check params and initialization
	if (info == NULL)
		return B_BAD_VALUE;

	int32 index = 0;
	switch (kind) {
		case B_APP_VERSION_KIND:
			index = 0;
			break;
		case B_SYSTEM_VERSION_KIND:
			index = 1;
			break;
		default:
			return B_BAD_VALUE;
	}

	if (InitCheck() != B_OK)
		return B_NO_INIT;

	// read the data
	size_t read = 0;
	version_info infos[2];
	status_t error = _ReadData(kVersionInfoAttribute, kVersionInfoResourceID,
		B_VERSION_INFO_TYPE, infos, 2 * sizeof(version_info), read);
	if (error != B_OK)
		return error;

	// check the read data
	if (read == sizeof(version_info)) {
		// only the app version info is there -- return a cleared system info
		if (index == 0)
			*info = infos[index];
		else if (index == 1)
			memset(info, 0, sizeof(version_info));
	} else if (read == 2 * sizeof(version_info)) {
		*info = infos[index];
	} else
		return B_ERROR;

	// return result
	return B_OK;
}


status_t
BAppFileInfo::SetVersionInfo(const version_info* info, version_kind kind)
{
	// check initialization
	status_t error = B_OK;
	if (InitCheck() != B_OK)
		error = B_NO_INIT;
	if (error == B_OK) {
		if (info != NULL) {
			// check param
			int32 index = 0;
			if (error == B_OK) {
				switch (kind) {
					case B_APP_VERSION_KIND:
						index = 0;
						break;
					case B_SYSTEM_VERSION_KIND:
						index = 1;
						break;
					default:
						error = B_BAD_VALUE;
						break;
				}
			}
			// read both infos
			version_info infos[2];
			if (error == B_OK) {
				size_t read;
				if (_ReadData(kVersionInfoAttribute, kVersionInfoResourceID,
						B_VERSION_INFO_TYPE, infos, 2 * sizeof(version_info),
						read) == B_OK) {
					// clear the part that hasn't been read
					if (read < sizeof(infos))
						memset((char*)infos + read, 0, sizeof(infos) - read);
				} else {
					// failed to read -- clear
					memset(infos, 0, sizeof(infos));
				}
			}
			infos[index] = *info;
			// write the data
			if (error == B_OK) {
				error = _WriteData(kVersionInfoAttribute,
					kVersionInfoResourceID, B_VERSION_INFO_TYPE, infos,
					2 * sizeof(version_info));
			}
		} else
			error = _RemoveData(kVersionInfoAttribute, B_VERSION_INFO_TYPE);
	}
	return error;
}


status_t
BAppFileInfo::GetIconForType(const char* type, BBitmap* icon, icon_size size)
	const
{
	if (InitCheck() != B_OK)
		return B_NO_INIT;

	if (icon == NULL || icon->InitCheck() != B_OK)
		return B_BAD_VALUE;

	// TODO: for consistency with attribute based icon reading, we
	// could also prefer B_CMAP8 icons here if the provided bitmap
	// is in that format. Right now, an existing B_CMAP8 icon resource
	// would be ignored as soon as a vector icon is present. On the other
	// hand, maybe this still results in a more consistent user interface,
	// since Tracker/Deskbar would surely show the vector icon.

	// try vector icon first
	BString vectorAttributeName(kIconAttribute);

	// check type param
	if (type != NULL) {
		if (BMimeType::IsValid(type))
			vectorAttributeName += type;
		else
			return B_BAD_VALUE;
	} else {
		vectorAttributeName += kIconType;
	}
	const char* attribute = vectorAttributeName.String();

	size_t bytesRead;
	void* allocatedBuffer;
	status_t error = _ReadData(attribute, -1, B_VECTOR_ICON_TYPE, NULL, 0,
		bytesRead, &allocatedBuffer);
	if (error == B_OK) {
		error = BIconUtils::GetVectorIcon((uint8*)allocatedBuffer,
										  bytesRead, icon);
		free(allocatedBuffer);
		return error;
	}

	// no vector icon if we got this far,
	// align size argument just in case
	if (size < B_LARGE_ICON)
		size = B_MINI_ICON;
	else
		size = B_LARGE_ICON;

	error = B_OK;
	// set some icon size related variables
	BString attributeString;
	BRect bounds;
	uint32 attrType = 0;
	size_t attrSize = 0;
	switch (size) {
		case B_MINI_ICON:
			attributeString = kMiniIconAttribute;
			bounds.Set(0, 0, 15, 15);
			attrType = B_MINI_ICON_TYPE;
			attrSize = 16 * 16;
			break;
		case B_LARGE_ICON:
			attributeString = kLargeIconAttribute;
			bounds.Set(0, 0, 31, 31);
			attrType = B_LARGE_ICON_TYPE;
			attrSize = 32 * 32;
			break;
		default:
			return B_BAD_VALUE;
	}

	// compose attribute name
	attributeString += type != NULL ? type : kStandardIconType;
	attribute = attributeString.String();

	// check parameters
	// currently, scaling B_CMAP8 icons is not supported
	if (icon->ColorSpace() == B_CMAP8 && icon->Bounds() != bounds)
		return B_BAD_VALUE;

	// read the data
	if (error == B_OK) {
		bool tempBuffer
			= icon->ColorSpace() != B_CMAP8 || icon->Bounds() != bounds;
		uint8* buffer = NULL;
		size_t read;
		if (tempBuffer) {
			// other color space or bitmap size than stored in attribute
			buffer = new(std::nothrow) uint8[attrSize];
			if (!buffer) {
				error = B_NO_MEMORY;
			} else {
				error = _ReadData(attribute, -1, attrType, buffer, attrSize,
					read);
			}
		} else {
			error = _ReadData(attribute, -1, attrType, icon->Bits(), attrSize,
				read);
		}
		if (error == B_OK && read != attrSize)
			error = B_ERROR;
		if (tempBuffer) {
			// other color space than stored in attribute
			if (error == B_OK) {
				error = BIconUtils::ConvertFromCMAP8(buffer, (uint32)size,
					(uint32)size, (uint32)size, icon);
			}
			delete[] buffer;
		}
	}
	return error;
}


status_t
BAppFileInfo::GetIconForType(const char* type, uint8** data, size_t* size) const
{
	if (InitCheck() != B_OK)
		return B_NO_INIT;

	if (data == NULL || size == NULL)
		return B_BAD_VALUE;

	// get vector icon
	BString attributeName(kIconAttribute);

	// check type param
	if (type != NULL) {
		if (BMimeType::IsValid(type))
			attributeName += type;
		else
			return B_BAD_VALUE;
	} else
		attributeName += kIconType;

	void* allocatedBuffer = NULL;
	status_t ret = _ReadData(attributeName.String(), -1, B_VECTOR_ICON_TYPE,
		NULL, 0, *size, &allocatedBuffer);

	if (ret < B_OK)
		return ret;

	*data = (uint8*)allocatedBuffer;
	return B_OK;
}


status_t
BAppFileInfo::SetIconForType(const char* type, const BBitmap* icon,
	icon_size which, bool updateMimeDB)
{
	status_t error = B_OK;

	// set some icon size related variables
	BString attributeString;
	BRect bounds;
	uint32 attrType = 0;
	size_t attrSize = 0;
	int32 resourceID = 0;
	switch (which) {
		case B_MINI_ICON:
			attributeString = kMiniIconAttribute;
			bounds.Set(0, 0, 15, 15);
			attrType = B_MINI_ICON_TYPE;
			attrSize = 16 * 16;
			resourceID = type != NULL
				? kMiniIconForTypeResourceID : kMiniIconResourceID;
			break;
		case B_LARGE_ICON:
			attributeString = kLargeIconAttribute;
			bounds.Set(0, 0, 31, 31);
			attrType = B_LARGE_ICON_TYPE;
			attrSize = 32 * 32;
			resourceID = type != NULL
				? kLargeIconForTypeResourceID : kLargeIconResourceID;
			break;
		default:
			error = B_BAD_VALUE;
			break;
	}

	// check type param
	if (error == B_OK) {
		if (type != NULL) {
			if (BMimeType::IsValid(type))
				attributeString += type;
			else
				error = B_BAD_VALUE;
		} else
			attributeString += kStandardIconType;
	}
	const char* attribute = attributeString.String();

	// check parameter and initialization
	if (error == B_OK && icon != NULL
		&& (icon->InitCheck() != B_OK || icon->Bounds() != bounds)) {
		error = B_BAD_VALUE;
	}
	if (error == B_OK && InitCheck() != B_OK)
		error = B_NO_INIT;

	// write/remove the attribute
	if (error == B_OK) {
		if (icon != NULL) {
			bool otherColorSpace = (icon->ColorSpace() != B_CMAP8);
			if (otherColorSpace) {
				BBitmap bitmap(bounds, B_BITMAP_NO_SERVER_LINK, B_CMAP8);
				error = bitmap.InitCheck();
				if (error == B_OK)
					error = bitmap.ImportBits(icon);
				if (error == B_OK) {
					error = _WriteData(attribute, resourceID, attrType,
						bitmap.Bits(), attrSize, true);
				}
			} else {
				error = _WriteData(attribute, resourceID, attrType,
					icon->Bits(), attrSize, true);
			}
		} else	// no icon given => remove
			error = _RemoveData(attribute, attrType);
	}

	// set the attribute on the MIME type, if the file has a signature
	BMimeType mimeType;
	if (updateMimeDB && error == B_OK && GetMetaMime(&mimeType) == B_OK) {
		if (!mimeType.IsInstalled())
			error = mimeType.Install();
		if (error == B_OK)
			error = mimeType.SetIconForType(type, icon, which);
	}
	return error;
}


status_t
BAppFileInfo::SetIconForType(const char* type, const BBitmap* icon,
	icon_size which)
{
	return SetIconForType(type, icon, which, true);
}


status_t
BAppFileInfo::SetIconForType(const char* type, const uint8* data, size_t size,
	bool updateMimeDB)
{
	if (InitCheck() != B_OK)
		return B_NO_INIT;

	// set some icon related variables
	BString attributeString = kIconAttribute;
	int32 resourceID = type ? kIconForTypeResourceID : kIconResourceID;
	uint32 attrType = B_VECTOR_ICON_TYPE;

	// check type param
	if (type != NULL) {
		if (BMimeType::IsValid(type))
			attributeString += type;
		else
			return B_BAD_VALUE;
	} else
		attributeString += kIconType;

	const char* attribute = attributeString.String();

	status_t error;
	// write/remove the attribute
	if (data != NULL)
		error = _WriteData(attribute, resourceID, attrType, data, size, true);
	else	// no icon given => remove
		error = _RemoveData(attribute, attrType);

	// set the attribute on the MIME type, if the file has a signature
	BMimeType mimeType;
	if (updateMimeDB && error == B_OK && GetMetaMime(&mimeType) == B_OK) {
		if (!mimeType.IsInstalled())
			error = mimeType.Install();
		if (error == B_OK)
			error = mimeType.SetIconForType(type, data, size);
	}
	return error;
}


status_t
BAppFileInfo::SetIconForType(const char* type, const uint8* data, size_t size)
{
	return SetIconForType(type, data, size, true);
}


void
BAppFileInfo::SetInfoLocation(info_location location)
{
	// if the resources failed to initialize, we must not use them
	if (fResources == NULL)
		location = info_location(location & ~B_USE_RESOURCES);

	fWhere = location;
}

bool
BAppFileInfo::IsUsingAttributes() const
{
	return (fWhere & B_USE_ATTRIBUTES) != 0;
}


bool
BAppFileInfo::IsUsingResources() const
{
	return (fWhere & B_USE_RESOURCES) != 0;
}


// FBC
void BAppFileInfo::_ReservedAppFileInfo1() {}
void BAppFileInfo::_ReservedAppFileInfo2() {}
void BAppFileInfo::_ReservedAppFileInfo3() {}


#ifdef __HAIKU_BEOS_COMPATIBLE
//!	Privatized assignment operator to prevent usage.
BAppFileInfo&
BAppFileInfo::operator=(const BAppFileInfo&)
{
	return *this;
}


//! Privatized copy constructor to prevent usage.
BAppFileInfo::BAppFileInfo(const BAppFileInfo&)
{
}
#endif


/*!	Initializes a BMimeType to the signature of the associated file.

	\warning The parameter \a meta is not checked.

	\param meta A pointer to a pre-allocated BMimeType that shall be
		   initialized to the signature of the associated file.

	\returns A status code.
	\retval B_OK Everything went fine.
	\retval B_BAD_VALUE \c NULL \a meta
	\retval B_ENTRY_NOT_FOUND The file has not signature or the signature is
	        (not installed in the MIME database.) no valid MIME string.
*/
status_t
BAppFileInfo::GetMetaMime(BMimeType* meta) const
{
	char signature[B_MIME_TYPE_LENGTH];
	status_t error = GetSignature(signature);
	if (error == B_OK)
		error = meta->SetTo(signature);
	else if (error == B_BAD_VALUE)
		error = B_ENTRY_NOT_FOUND;
	if (error == B_OK && !meta->IsValid())
		error = B_BAD_VALUE;
	return error;
}


/*!	Reads data from an attribute or resource.

	\note The data is read from the location specified by \a fWhere.

	\warning The object must be properly initialized. The parameters are
		\b NOT checked.

	\param name The name of the attribute/resource to be read.
	\param id The resource ID of the resource to be read. It is ignored
		   when < 0.
	\param type The type of the attribute/resource to be read.
	\param buffer A pre-allocated buffer for the data to be read.
	\param bufferSize The size of the supplied buffer.
	\param bytesRead A reference parameter, set to the number of bytes
		   actually read.
	\param allocatedBuffer If not \c NULL, the method allocates a buffer
		   large enough too store the whole data and writes a pointer to it
		   into this variable. If \c NULL, the supplied buffer is used.

	\returns A status code.
	\retval B_OK Everything went fine.
	\retval B_ENTRY_NOT_FOUND The entry was not found.
	\retval B_NO_MEMORY Ran out of memory allocating the buffer.
	\retval B_BAD_VALUE \a type did not match.
*/
status_t
BAppFileInfo::_ReadData(const char* name, int32 id, type_code type,
	void* buffer, size_t bufferSize, size_t& bytesRead, void** allocatedBuffer)
	const
{
	status_t error = B_OK;

	if (allocatedBuffer)
		buffer = NULL;

	bool foundData = false;

	if (IsUsingAttributes()) {
		// get an attribute info
		attr_info info;
		if (error == B_OK)
			error = fNode->GetAttrInfo(name, &info);

		// check type and size, allocate a buffer, if required
		if (error == B_OK && info.type != type)
			error = B_BAD_VALUE;
		if (error == B_OK && allocatedBuffer != NULL) {
			buffer = malloc(info.size);
			if (buffer == NULL)
				error = B_NO_MEMORY;
			bufferSize = info.size;
		}
		if (error == B_OK && (off_t)bufferSize < info.size)
			error = B_BAD_VALUE;

		// read the data
		if (error == B_OK) {
			ssize_t read = fNode->ReadAttr(name, type, 0, buffer, info.size);
			if (read < 0)
				error = read;
			else if (read != info.size)
				error = B_ERROR;
			else
				bytesRead = read;
		}

		foundData = error == B_OK;

		// free the allocated buffer on error
		if (!foundData && allocatedBuffer != NULL && buffer != NULL) {
			free(buffer);
			buffer = NULL;
		}
	}

	if (!foundData && IsUsingResources()) {
		// get a resource info
		error = B_OK;
		int32 idFound;
		size_t sizeFound;
		if (error == B_OK) {
			if (!fResources->GetResourceInfo(type, name, &idFound, &sizeFound))
				error = B_ENTRY_NOT_FOUND;
		}

		// check id and size, allocate a buffer, if required
		if (error == B_OK && id >= 0 && idFound != id)
			error = B_ENTRY_NOT_FOUND;
		if (error == B_OK && allocatedBuffer) {
			buffer = malloc(sizeFound);
			if (!buffer)
				error = B_NO_MEMORY;
			bufferSize = sizeFound;
		}
		if (error == B_OK && bufferSize < sizeFound)
			error = B_BAD_VALUE;

		// load resource
		const void* resourceData = NULL;
		if (error == B_OK) {
			resourceData = fResources->LoadResource(type, name, &bytesRead);
			if (resourceData != NULL && sizeFound == bytesRead)
				memcpy(buffer, resourceData, bytesRead);
			else
				error = B_ERROR;
		}
	} else if (!foundData)
		error = B_BAD_VALUE;

	// return the allocated buffer, or free it on error
	if (allocatedBuffer != NULL) {
		if (error == B_OK)
			*allocatedBuffer = buffer;
		else
			free(buffer);
	}

	return error;
}


/*!	Writes data to an attribute or resource.

	\note The data is written to the location(s) specified by \a fWhere.

	\warning The object must be properly initialized. The parameters are
		\b NOT checked.

	\param name The name of the attribute/resource to be written.
	\param id The resource ID of the resource to be written.
	\param type The type of the attribute/resource to be written.
	\param buffer A buffer containing the data to be written.
	\param bufferSize The size of the supplied buffer.
	\param findID If set to \c true use the ID that is already assigned to the
		   \a name / \a type pair or take the first unused ID >= \a id.
		   If \c false, \a id is used.

	\returns A status code.
	\retval B_OK Everything went fine.
	\retval B_ERROR An error occurred while trying to write the data.
*/
status_t
BAppFileInfo::_WriteData(const char* name, int32 id, type_code type,
	const void* buffer, size_t bufferSize, bool findID)
{
	if (!IsUsingAttributes() && !IsUsingResources())
		return B_NO_INIT;

	status_t error = B_OK;

	// write to attribute
	if (IsUsingAttributes()) {
		ssize_t written = fNode->WriteAttr(name, type, 0, buffer, bufferSize);
		if (written < 0)
			error = written;
		else if (written != (ssize_t)bufferSize)
			error = B_ERROR;
	}
	// write to resource
	if (IsUsingResources() && error == B_OK) {
		if (findID) {
			// get the resource info
			int32 idFound;
			size_t sizeFound;
			if (fResources->GetResourceInfo(type, name, &idFound, &sizeFound))
				id = idFound;
			else {
				// type-name pair doesn't exist yet -- find unused ID
				while (fResources->HasResource(type, id))
					id++;
			}
		}
		error = fResources->AddResource(type, id, buffer, bufferSize, name);
	}
	return error;
}


/*!	Removes an attribute or resource.

	\note The removal location is specified by \a fWhere.

	\warning The object must be properly initialized. The parameters are
		\b NOT checked.

	\param name The name of the attribute/resource to be remove.
	\param type The type of the attribute/resource to be removed.

	\returns A status code.
	\retval B_OK Everything went fine.
	\retval B_NO_INIT Not using attributes and not using resources.
	\retval B_ENTRY_NOT_FOUND The attribute or resource was not found.
*/
status_t
BAppFileInfo::_RemoveData(const char* name, type_code type)
{
	if (!IsUsingAttributes() && !IsUsingResources())
		return B_NO_INIT;

	status_t error = B_OK;

	// remove the attribute
	if (IsUsingAttributes()) {
		error = fNode->RemoveAttr(name);
		// It's no error, if there has been no attribute.
		if (error == B_ENTRY_NOT_FOUND)
			error = B_OK;
	}
	// remove the resource
	if (IsUsingResources() && error == B_OK) {
		// get a resource info
		int32 idFound;
		size_t sizeFound;
		if (fResources->GetResourceInfo(type, name, &idFound, &sizeFound))
			error = fResources->RemoveResource(type, idFound);
	}
	return error;
}
